package framework.core
{
import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.events.Event;
import flash.events.FocusEvent;
import flash.events.IEventDispatcher;
import flash.events.MouseEvent;
import flash.utils.Dictionary;
import flash.utils.describeType;

import framework.view.components.Component;
import framework.view.components.IComponent;

public class ComponentParser
{
	private static var FLASH_EVENTS:Dictionary;
    
    private static var CLASS_XML_MAP:Dictionary = new Dictionary(true);
	
	private var COMPONENT_EVENTS:Dictionary;
	
	private var nsMap:Dictionary = new Dictionary(true); /* Namespace */
	
	private var topComponent:IComponent;
	
	private var listeners:Array = [];
	
	private var _properties:Dictionary = new Dictionary(true);

	public function get properties():Dictionary
	{
		return _properties;
	}

	public function set properties(value:Dictionary):void
	{
		_properties = value;
	}
	
	private var _children:Dictionary = new Dictionary(true);

	public function get children():Dictionary
	{
		return _children;
	}

	public function set children(value:Dictionary):void
	{
		_children = value;
	}
	
	public function parse(component:Component):void
	{
		if (!component.xmlDoc) return;
		
		topComponent = component;
		
		var xml:XML = component.xmlDoc;
		
		for each (var ns:Namespace in xml.namespaceDeclarations())
		{
			nsMap[ns.prefix] = ns.uri;
		}
		//init properties
		initProperty(xml, component);
		
		//render children
		for each (var item:XML in xml.children())
		{
			render(item, component);
		}
	}
	
	public function dispose():void
	{
		for each (var listener:Listener in listeners)
		{
			listener.dispose();
		}		
		listeners = null;
		
		for each (var comp:Object in children)
		{
			if (comp.hasOwnProperty("dispose"))
			{
				comp.dispose();
			}
		}
		children = null;
		properties = null;
	}
	
	private function initProperty(xml:XML, component:*):void
	{
		var events:Dictionary = new Dictionary(true);
		var doc:*;
        /*
        if (component is Component && Component(component).cacheClassDesc == true)
        {
            var mapKey:String = component.toString();
            doc = CLASS_XML_MAP[mapKey];
            if (!doc)
            {
                doc = describeType(component);
                CLASS_XML_MAP[mapKey] = doc;
            }
        }
        else
        {
            doc = describeType(component);
        }
        */
        var mapKey:String = component.toString();
        doc = CLASS_XML_MAP[mapKey];
        if (!doc)
        {
            doc = describeType(component);
            CLASS_XML_MAP[mapKey] = doc;
        }
        
		doc = doc.metadata ? doc.metadata.(@name == "Event") : null;
		
		var obj:*;
		if (doc)
		{
			for each (var meta:* in doc)
			{
				obj = {name: String(meta.arg.(@key == "name").@value), 
					   type: String(meta.arg.(@key == "type").@value)};
				events[obj.name] = obj;
			}
		}
		obj = null;
		
		if (!FLASH_EVENTS)
		{
			FLASH_EVENTS = new Dictionary(true);
			var evtDoc:XML = describeType(Event);
			var evt:*;
			for each (var constant1:* in evtDoc.constant)
			{
				evt = {name: Event[String(constant1.@name)], 
					   type: "flash.events.Event" };
				FLASH_EVENTS[evt.name] = evt;
			}
			
			evtDoc = describeType(MouseEvent);
			
			for each (var constant2:* in evtDoc.constant)
			{
				evt = {name: MouseEvent[String(constant2.@name)], 
					   type: "flash.events.MouseEvent" };
				FLASH_EVENTS[evt.name] = evt;
			}
            
            evtDoc = describeType(FocusEvent);
            
            for each (var constant3:* in evtDoc.constant)
            {
                evt = {name: FocusEvent[String(constant3.@name)], 
                    type: "flash.events.FocusEvent" };
                FLASH_EVENTS[evt.name] = evt;
            }
			evt = null;
			evtDoc = null;
		}	
		
        var attributes:* = xml.attributes();
		for each (var a:* in attributes)
		{
			var p:String = a.name();
			var value:String = a;
			
			if (events[p] || FLASH_EVENTS[p])
			{
				var evtObj:* = events[p] ? events[p] : FLASH_EVENTS[p];
				IEventDispatcher(component).addEventListener(evtObj.name, topComponent[value]);
				
				var listener:Listener = new Listener();
				listener.dispatcher = IEventDispatcher(component);
				listener.event = evtObj.name;
				listener.handler = topComponent[value];
				listeners.push(listener);
				
				continue;
			}
			
			//set variable
			if (p == "id")
			{			
				properties[value] = component;
				if (Object(topComponent).hasOwnProperty(value))
				{
					topComponent[value] = component;
				}
			}
			if (component != topComponent) children[component] = component;
			
			if (Object(component).hasOwnProperty(p))
			{
				if (value.indexOf("{") == 0 && value.indexOf("}") == value.length - 1)
				{
					var variable:String = value.substring(1, value.length - 1);
					var variables:Array = variable.split(".");
					var varObj:*;
					if (variables.length > 1)
					{
						if (Object(topComponent).hasOwnProperty(variables[0]))
						{
							varObj = topComponent[variables[0]];
						}
						else if (properties[variables[0]] != null)
						{
							varObj = properties[variables[0]];
						}
						else
						{
							
						}
						for (var i:int = 1; i < variables.length; i++)
						{
							varObj = varObj[variables[i]];
						}
					}
					else
					{
						varObj = Object(topComponent).hasOwnProperty(variable) ? topComponent[variable] : properties[variable];
					}
					component[p] = varObj;
					continue;
				}
				
				if (value.indexOf("Class::") == 0)
				{
					component[p] = ComponentBuilder.getClassByName(value.split("::")[1]);
					continue;
				}
				
				if (component[p] is Boolean)
				{
					component[p] = value == "true" ? true : false;
				}
				else
				{
					component[p] = value;
				}
			}
		}
		
		if (component is IComponent)
		{
			IComponent(component).commitProperty();
		}
	}
	
	private function render(xml:XML, parent:*, isDeclaration:Boolean = false):void
	{
		var qn:QName = xml.name();
		var ref:String = qn.uri + "."+ qn.localName;
		var comp:*;
		
		if (qn.localName == "declaration")
		{
			for each (var declaration:XML in xml.children())
			{
				render(declaration, null, true);
			}
			return;
		}
		
		if (isDeclaration)
		{
			comp = ComponentBuilder.createNonDisplayObject(ref);
		}
		else
		{
			var id:String = String(xml.@id);
			if (id != "" && Object(topComponent).hasOwnProperty(qn.localName) &&
				topComponent[qn.localName] != null && 
				topComponent[qn.localName] is DisplayObject)
			{
				comp = topComponent[qn.localName];
			}
			else
			{
				comp = ComponentBuilder.createByDefineName(ref);
			}
		}
		
		if (parent is DisplayObjectContainer && comp is DisplayObject)
		{			
			if (!comp.parent)
			{
				DisplayObjectContainer(parent).addChild(comp as DisplayObject);
			}			
		}
		initProperty(xml, comp);
		
		for each (var item:XML in xml.children())
		{
			render(item, comp);
		}
		
	}
}
}

import flash.events.IEventDispatcher;


class Listener
{
	public var dispatcher:IEventDispatcher;
	
	public var event:String;
	
	public var handler:Function;
	
	public function dispose():void
	{
		dispatcher.removeEventListener(event, handler);
	}
}

